package strmap

import (
	"bytes"
	gobase "gitee.com/ymofen/golang/gobase"
	"io"
	"net/url"
	"sort"
	"strings"
	"time"
)

const (
	EMPTY_STRING      string = ""
	QUOTECHR                 = '"'
	CHAR_QUOTE_DOUBLE        = '"'
	CHAR_QUOTE_SINGLE        = '\''
	CHAR_SLASH               = '\\'
)

type StrMap map[string]string

func NewStrMap() StrMap {
	return make(StrMap)
}

func NewStrMapFromURLStr(s string) StrMap {
	strmap := NewStrMap()
	strmap.URLFormDecode(s)
	return strmap
}

func NewStrMapEx(s, kvsep, itmsep string) StrMap {
	r := make(StrMap)
	r.ParseKVPairs(s, kvsep, itmsep)
	return r
}

func (this StrMap) Reset() {
	for k := range this {
		delete(this, k)
	}
}

// ExecReplace 利用映射关系进行替换， key -> value
func (this StrMap) ExecReplace(s string) string {
	for k, v := range this {
		s = gobase.ReplaceAll(s, k, v)
	}
	return s
}

func (this StrMap) SortRange(fn func(k, v string) bool) {
	if this == nil {
		return
	}
	keys := make([]string, 0, len(this))
	for k := range this {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		v := this[k]
		if !fn(k, v) {
			break
		}
	}
}

func (this StrMap) SortRangeEncode(kvspliter string, itmspliter string, fn func(k, v string) bool) string {
	if this == nil {
		return ""
	}
	var buf strings.Builder
	this.SortRange(func(k, v string) bool {
		if fn(k, v) {
			if buf.Len() > 0 {
				buf.WriteString(itmspliter)
			}
			buf.WriteString(k)
			buf.WriteString(kvspliter)
			buf.WriteString(v)
		}
		return true
	})
	return buf.String()

}

func (this StrMap) Encode(kvspliter string, itmspliter string) string {
	if this == nil {
		return ""
	}
	var buf strings.Builder
	keys := make([]string, 0, len(this))
	for k := range this {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		if buf.Len() > 0 {
			buf.WriteString(itmspliter)
		}
		v := this[k]
		buf.WriteString(k)
		buf.WriteString(kvspliter)
		buf.WriteString(v)
	}
	return buf.String()
}

func MapEncode(strMap map[string]string, kvsep, itmsep string, escapeflag bool, bb io.Writer) {
	keys := make([]string, 0, len(strMap))
	for k := range strMap {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	i := 0
	for _, k := range keys {
		v := strMap[k]
		if i > 0 {
			bb.Write([]byte(itmsep))
		}
		if escapeflag {
			keyEscaped := url.QueryEscape(k)
			bb.Write([]byte(keyEscaped))
			bb.Write([]byte(kvsep))
			bb.Write([]byte(url.QueryEscape(v)))
		} else {
			bb.Write([]byte(k))
			bb.Write([]byte(kvsep))
			bb.Write([]byte(v))
		}
		i++
	}
}

func (this StrMap) DeleteByKeylst(keylst ...string) (r int) {
	for _, k := range keylst {
		delete(this, k)
		r++
	}
	return
}

func (this StrMap) SortRangeEncodeEx(kvspliter string, itmspliter string, fn func(k, v string) (k1, v1 string, ok bool)) string {
	if this == nil {
		return ""
	}
	var buf strings.Builder
	kvlst := make([][]string, 0, len(this))
	for k, v := range this {
		if k1, v1, ok := fn(k, v); ok {
			kvlst = append(kvlst, []string{k1, v1})
		}
	}
	sort.Slice(kvlst, func(i, j int) bool {
		return kvlst[i][0] < kvlst[j][0]
	})

	for _, kv := range kvlst {
		if buf.Len() > 0 {
			buf.WriteString(itmspliter)
		}
		buf.WriteString(kv[0])
		buf.WriteString(kvspliter)
		buf.WriteString(kv[1])
	}
	return buf.String()
}

func (this StrMap) SortRangeUrlEncode(fn func(k, v string) bool) string {
	return this.SortRangeEncodeEx("=", "&", func(k, v string) (k1, v1 string, ok bool) {
		ok = fn(k, v)
		if ok {
			k1 = url.QueryEscape(k)
			v1 = url.QueryEscape(v)
		}
		return
	})
}

func (this StrMap) SortRangeUrlEncode0(fn func(k, v string) bool) string {
	return this.SortRangeEncodeEx("=", "&", func(k, v string) (k1, v1 string, ok bool) {
		ok = fn(k, v)
		if ok {
			k1 = gobase.UrlEncode0(k)
			v1 = gobase.UrlEncode0(v)
		}
		return
	})
}

func (this StrMap) URLFormEncodeKeylst(keylst ...string) string {
	var sb strings.Builder
	for _, k := range keylst {
		if v, ok := this[k]; ok {
			if sb.Len() > 0 {
				sb.WriteByte('&')
			}
			keyEscaped := url.QueryEscape(k)
			sb.WriteString(keyEscaped)
			sb.WriteByte('=')
			sb.WriteString(url.QueryEscape(v))
		}
	}
	return sb.String()
}

// 最简单的方式进行编码
func (this StrMap) URLEncode0WithKeys(keylst ...string) string {
	if this == nil {
		return ""
	}
	var buf strings.Builder
	for _, k := range keylst {
		if v, ok := this[k]; ok {
			if buf.Len() > 0 {
				buf.WriteByte('&')
			}
			gobase.UrlEncode0ToWriter(&buf, k)
			buf.WriteByte('=')
			gobase.UrlEncode0ToWriter(&buf, v)
		}
	}
	return buf.String()
}

// 前缀或者后缀
type PartString string

func (this PartString) UrlEncodeTryTrimPrefix(k, v string) (k1, v1 string, ok bool) {
	k1, ok = gobase.TryTrimPrefix(k, string(this))
	if ok {
		k1 = gobase.UrlEncode0(k1)
		v1 = gobase.UrlEncode0(v)
	}
	return
}

func (this PartString) UrlEncodeTryPrefix(k, v string) (k1, v1 string, ok bool) {
	ok = strings.HasPrefix(k, string(this))
	if ok {
		k1 = gobase.UrlEncode0(k)
		v1 = gobase.UrlEncode0(v)
	}
	return
}

func (this PartString) UrlEncodeTryTrimSuffix(k, v string) (k1, v1 string, ok bool) {
	k1, ok = gobase.TryTrimSuffix(k, string(this))
	if ok {
		k1 = gobase.UrlEncode0(k1)
		v1 = gobase.UrlEncode0(v)
	}
	return
}

func (this PartString) UrlEncodeTrySuffix(k, v string) (k1, v1 string, ok bool) {
	ok = strings.HasSuffix(k, string(this))
	if ok {
		k1 = gobase.UrlEncode0(k)
		v1 = gobase.UrlEncode0(v)
	}
	return
}

func (this StrMap) URLEncode0Prefix(prefix string, trimPrefix bool) string {
	if trimPrefix {
		return this.SortRangeEncodeEx("=", "&", PartString(prefix).UrlEncodeTryTrimPrefix)
	} else {
		return this.SortRangeEncodeEx("=", "&", PartString(prefix).UrlEncodeTryPrefix)
	}
}

func (this StrMap) URLEncode0Suffix(prefix string, trimPrefix bool) string {
	if trimPrefix {
		return this.SortRangeEncodeEx("=", "&", PartString(prefix).UrlEncodeTryTrimSuffix)
	} else {
		return this.SortRangeEncodeEx("=", "&", PartString(prefix).UrlEncodeTrySuffix)
	}
}

// 最简单的方式进行编码
func (this StrMap) URLEncode0() string {
	if this == nil {
		return ""
	}
	var buf strings.Builder
	keys := make([]string, 0, len(this))
	for k := range this {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		v := this[k]
		if buf.Len() > 0 {
			buf.WriteByte('&')
		}
		gobase.UrlEncode0ToWriter(&buf, k)
		buf.WriteByte('=')
		gobase.UrlEncode0ToWriter(&buf, v)
	}
	return buf.String()
}

func (this StrMap) URLFormEncode() string {
	if this == nil {
		return ""
	}
	var buf strings.Builder
	keys := make([]string, 0, len(this))
	for k := range this {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		v := this[k]
		keyEscaped := url.QueryEscape(k)
		if buf.Len() > 0 {
			buf.WriteByte('&')
		}
		buf.WriteString(keyEscaped)
		buf.WriteByte('=')
		buf.WriteString(url.QueryEscape(v))
	}
	return buf.String()
}

func (this StrMap) URLFormDecode(s string) {
	lst := strings.Split(s, "&")
	if len(lst) == 0 {
		return
	}
	for i := 0; i < len(lst); i++ {
		k, v := gobase.Split2Str(lst[i], "=")
		k, _ = url.QueryUnescape(k)
		v, _ = url.QueryUnescape(v)
		if len(k) > 0 {
			this[k] = v
		}
	}
}

func (this StrMap) CopyFrom(src StrMap) {
	for k, v := range src {
		this[k] = v
	}
}

// range
func (this StrMap) RangePrefix(prefix string, fn func(k, v string) bool) {
	for k, v := range this {
		if k1, ok := gobase.TryTrimPrefix(k, prefix); ok {
			if !fn(k1, v) {
				return
			}
		}
	}
}

// 覆盖掉前一个值
func (this StrMap) CopyFromPrefix(src StrMap, prefix string, overrideFlag bool) {
	for k, v := range src {
		if k1, ok := gobase.TryTrimPrefix(k, prefix); ok {
			if overrideFlag {
				this[k1] = v
			} else {
				if _, ok1 := this[k1]; !ok1 {
					this[k1] = v
				}
			}
		}
	}
}

func (this StrMap) ParseKVPairsEx(s string, kvsep string, itmsep string, keylower bool) {
	lst := strings.Split(s, itmsep)
	if len(lst) == 0 {
		return
	}

	for i := 0; i < len(lst); i++ {
		k, v := gobase.Split2Str(lst[i], kvsep)
		if len(k) > 0 {
			if keylower {
				k = strings.ToLower(k)
			}
			this[k] = v
		}
	}
}

func (this StrMap) ParseKVPairs(s string, kvsep string, itmsep string) {
	lst := strings.Split(s, itmsep)
	if len(lst) == 0 {
		return
	}

	for i := 0; i < len(lst); i++ {
		k, v := gobase.Split2Str(lst[i], kvsep)
		k = gobase.Trim(k)
		if len(k) > 0 {
			this[k] = v
		}
	}
}

func (this StrMap) StringByName(k string, def string) string {
	if v, ok := this[k]; ok {
		return v
	} else {
		return def
	}
}

func (this StrMap) IntByName(k string, def int) int {
	if v, ok := this[k]; ok {
		if len(v) == 0 {
			return def
		}
		return gobase.StrToIntDef(v, def)
	} else {
		return def
	}
}

func (this StrMap) Float64ByName(k string, def float64) float64 {
	if v, ok := this[k]; ok {
		return gobase.StrToFloat64Def(v, def)
	} else {
		return def
	}
}

func (this StrMap) U8ByName(k string, def byte) byte {
	return byte(this.IntByName(k, int(def)))
}

func (this StrMap) Int8ByName(k string, def int8) int8 {
	return int8(this.IntByName(k, int(def)))
}

func (this StrMap) Int16ByName(k string, def int16) int16 {
	return int16(this.IntByName(k, int(def)))
}

func (this StrMap) UnixTimeByName(k string, def time.Time) time.Time {
	v := this.IntByName(k, -99999)
	if v == -99999 {
		return def
	} else {
		return time.Unix(int64(v), 0)
	}
}

/*
解析命令行
XX.exe -a=v
*/
func (this StrMap) ParseCmdArgs(args []string) {
	for i := 0; i < len(args); i++ {
		s1, s2 := gobase.Split2Str(args[i], "=")

		if len(s1) > 0 {
			if s1[0] == '-' {
				s1 = s1[1:]
			}
			if len(s2) == 0 {
				s2 = s1
				if i == 0 {
					this["0"] = s1
				} else {
					this[s1] = s2
				}
			} else {
				s2, _ = gobase.TryTrimQuoteChr(s2, QUOTECHR, QUOTECHR)
				this[s1] = s2
			}
		}
	}
}

func EscapeChar(c byte) byte {
	switch c {
	case 't':
		return '\t'
	default:
		return c
	}
}

// $1 spidx   ' ' 3, tokens[2] = " "
func ParseTokens(s string, sep byte) (tokens []string) {

	var eMarker byte = 0
	var tmp bytes.Buffer
	var slashFlag bool

	var nextFunc func(c byte)

	var nextForQuote = func(c byte) {
		if c == eMarker {
			tokens = append(tokens, tmp.String())
			tmp.Reset()
			nextFunc = nil
		} else {
			tmp.WriteByte(c)
		}
		return
	}

	for i := 0; i < len(s); {
		c := s[i]

		if slashFlag {
			tmp.WriteByte(EscapeChar(c))
			i++
			slashFlag = false
			continue
		}

		if c == CHAR_SLASH {
			slashFlag = true
			i++
			continue
		}

		if nextFunc != nil {
			nextFunc(c)
			i++
			continue
		}

		switch c {
		case sep:
			if tmp.Len() > 0 {
				tokens = append(tokens, tmp.String())
				tmp.Reset()
			}
			i++
			continue
		case CHAR_QUOTE_DOUBLE:
			eMarker = CHAR_QUOTE_DOUBLE
			nextFunc = nextForQuote
			i++
			continue
		case CHAR_QUOTE_SINGLE:
			eMarker = CHAR_QUOTE_SINGLE
			nextFunc = nextForQuote
			i++
			continue
		default:
			tmp.WriteByte(c)
			i++
		}
	}

	if tmp.Len() > 0 {
		tokens = append(tokens, tmp.String())
	}
	return
}
